# @FileName  : tool.py
# @Time      : 2022/4/10 0010  11:44
# @Author    : LuZhaoHui
# @Software  : PyCharm

import ast
import codecs
import csv
import datetime
import locale
import os
import re
import shutil
import time
from configparser import ConfigParser
import win32api
import win32con
#import ctypes

import natsort
import openpyxl
import xlsxwriter
from PIL import Image
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

Image.MAX_IMAGE_PIXELS = None
EXE_PATH = os.path.abspath(os.getcwd())
# XLS_EXT = (".xls", ".xlsx")
IMG_EXT = (".jpg")

# 图片信息：段标识(1-0xff)+段类型(1-IMG_XXXX)+段长度(2)+段内容(n)
# 段类型
IMG_MARK = 0xff  # 段标识
IMG_SOI = 0xd8  # 文件头
IMG_APP0 = 0xe0  # 定义交换格式和图像识别信息
IMG_APP1 = 0xe1  #
IMG_APPF = 0xef  #
IMG_DQT = 0xdb  # 定义量化表
IMG_DRI = 0xdd  # 定义重新开始间隔
IMG_SOF0 = 0xc0  # 帧开始
IMG_SOF1 = 0xc1
IMG_DHT = 0xc4  # 定义HUFFMAN表
IMG_SOS = 0xda  # 扫描行开始
IMG_EOI = 0xd9  # 文件尾
IMG_COM = 0xfe  # 注释

PAPER_LETTER = {'A0': (33.11, 46.81),
                'B1': (27.83, 39.37),
                'A1': (23.39, 33.11),
                'B2': (19.69, 27.83),
                'A2': (16.54, 23.39),
                'B3': (13.90, 19.69),
                'A3': (11.69, 16.54),
                'B4': (9.84, 13.90),
                'A4': (8.27, 11.69),
                'A5': (5.82, 8.26),
                'A6': (4.13, 5.82)}

def setFileHidden(file):
    if os.path.isfile(file):
        win32api.SetFileAttributes(file, win32con.FILE_ATTRIBUTE_HIDDEN)

def setFileNormal(file):
    if os.path.isfile(file):
        win32api.SetFileAttributes(file, win32con.FILE_ATTRIBUTE_NORMAL)

def logo(img):
    return os.path.join(EXE_PATH, img).replace('\\', '/')

def getPaper(s, d):
    if d[0] > 0 and d[1] > 0:
        w = s[0] / d[0]
        h = s[1] / d[1]
        for k, v in PAPER_LETTER.items():
            v0 = v[0] / 10
            v1 = v[1] / 10
            if (v[0] - v0) < w < (v[0] + v0) and (v[1] - v1) < h < (v[1] + v1):
                return k
            if (v[0] - v0) < h < (v[0] + v0) and (v[1] - v1) < w < (v[1] + v1):
                return k
    return ''


def getDateTime(strDateTime):
    return QDateTime.fromString(strDateTime, 'yyyy-MM-dd hh:mm:ss')


def getDate(strDate):
    return QDate.fromString(strDate, 'yyyy-MM-dd')


def getTime(strTime):
    return QTime.fromString(strTime, 'hh:mm:ss')


def dateTimeToStr(datetime):
    return datetime.dateTime().toString('yyyy-MM-dd hh:mm:ss')


def dateToStr(date):
    return date.date().toString('yyyy-MM-dd')


def timeToStr(time):
    return time.time().toString('hh:mm:ss')


def nowDateTimeStr():
    return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')


def nowDateStr():
    return datetime.datetime.now().strftime('%Y-%m-%d')


def nowTimeStr():
    return datetime.datetime.now().strftime('%H:%M:%S')


def copyFile(src, des):
    dir = os.path.dirname(des)
    if not os.path.exists(dir):
        os.makedirs(dir)
    return shutil.copy(src, des)


def deleteFile(name):
    if os.path.exists(name):
        os.remove(name)

def showBox(object, msg, title='提示'):
    QMessageBox.about(object, title, '     %s     ' % (msg))

def showBoxYes(object, msg, title='提示'):
    reply = QMessageBox.question(object,
                                 title,
                                 '     %s     ' % (msg),
                                 QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
    if reply == QMessageBox.Yes:
        return True
    return False

def natSort(flist):
    locale.setlocale(locale.LC_ALL, 'zh_CN.UTF-8')
    return natsort.natsorted(flist,
                             # key=lambda x: x.replace('(', '@').replace(')', ''),
                             key=lambda x: x.split('-'),
                             alg=natsort.ns.PATH | natsort.ns.LOCALE | natsort.ns.IGNORECASE)


def file_AB(name):
    dir = os.path.dirname(name)
    f = os.path.basename(name)
    f1 = os.path.splitext(f)[0]
    f2 = os.path.splitext(f)[1]
    a = ''
    b = ''
    if f1[-1] == 'a':
        a = 'a'
        b = 'b'
    else:
        if f1[-1] == 'b':
            a = 'b'
            b = 'a'
    if f1[-1] == 'A':
        a = 'A'
        b = 'B'
    else:
        if f1[-1] == 'B':
            a = 'B'
            b = 'A'
    if a != '' and b != '':
        f1 = f1[::-1].replace(a, b, 1)[::-1]
    return pathJoin(dir, f1 + f2)


def has_other(text):
    return re.search('^[a-zA-Z0-9-]+$', text)


def has_hz(text):
    for c in text:
        if u'\u4e00' <= c <= u'\u9fff':
            return True
    return False


def saveLog(msg, path, log=''):
    if not os.path.exists(path):
        os.makedirs(path)
    if len(log) == 0:
        file = path + '/log.txt'
    else:
        file = path + '/' + log
    if len(msg) > 0:
        with open(file, 'a', encoding='utf-8') as f:
            str = nowDateStr() + ' ' + nowTimeStr() + ' ' + msg + '\n'
            f.write(str)
        return str
    else:
        f = open(file, 'w', encoding='utf-8')
        f.close()
        return ''


def setComboBoxHeight(combo, h):
    combo.setStyleSheet("QAbstractItemView::item{height: %dpx;}" % (h))
    combo.setView(QListView())


def pathJoin(path1, path2):
    return os.path.join(path1, path2).replace('\\', '/')


def getImg0File(file):
    size = [0, 0]
    unit = 0
    density = [0, 0]
    dot = [0, 0]
    with open(file, 'rb') as f:
        l = 64 * 1024
        data = f.read(l)
        i = 0
        while i < l:
            # i += 1
            if data[i] == IMG_MARK:
                i += 1
                continue
            elif data[i] == IMG_APP0:
                # 图像识别信息
                i += 1
                ch = data[i]
                cl = data[i + 1]
                len = ch * 256 + cl
                j = i + 2
                j += 5
                j += 2
                unit = data[j]
                j += 1
                ch = data[j]
                cl = data[j + 1]
                density[0] = ch * 256 + cl
                j += 2
                ch = data[j]
                cl = data[j + 1]
                density[1] = ch * 256 + cl
                j += 2
                dot[0] = data[j]
                j += 1
                dot[1] = data[j]
                i += len
                continue
            elif data[i] == IMG_COM or (data[i] >= IMG_APP1 and data[i] <= IMG_APPF):
                # 图像识别信息
                i += 1
                ch = data[i]
                cl = data[i + 1]
                len = ch * 256 + cl
                i += len
                continue
            elif data[i] == IMG_SOI:
                i += 1
                continue
            elif data[i] == IMG_DQT:
                i += 1
                ch = data[i]
                cl = data[i + 1]
                len = ch * 256 + cl
                i += len
                continue
            elif data[i] == IMG_SOF0:
                # 图像基本信息:段标识(1,FF),段类型(1,C0),段长度(2,8＋组件数量×3)
                i += 3
                # 样本精度(1,8),图片高度(2),图片宽度(2)
                i += 1
                ch = data[i]
                i += 1
                cl = data[i]
                size[1] = ch * 256 + cl
                i += 1
                ch = data[i]
                i += 1
                cl = data[i]
                size[0] = ch * 256 + cl
                # 组件数量(1,1＝灰度图，3＝YCbCr / YIQ,4＝CMYK),（以下每个组件占用３字节）
                break
            else:
                break
    return size + density


def GetFileByte(file):
    info = QFileInfo(file)
    byte = info.size()
    return byte


def showLabelImage(area, label, name, self):
    img = QImage()
    img.load(name)
    if self:
        w = area.width()
        h = area.height()
        new_img = img.scaled(w, h, Qt.KeepAspectRatio)
        label.setPixmap(QPixmap.fromImage(new_img))
    else:
        label.setPixmap(QPixmap.fromImage(img))

def imageRotate(name,rotate):
    # 0-水平翻转，1-垂直翻转，2-180度翻转，3-270度翻转
    image = Image.open(name)
    image = image.transpose(rotate)
    image.save(name, dpi=(300.0, 300.0))

def isImgOk(info):
    return 'Ok' if info[0] > 0 and info[1] > 0 and info[2] > 0 else 'Err'

def getColor(color):
    if color == 'RGB':
        return '彩色'
    if color == 'L':
        return '灰度'
    return '黑白'

def getImgFile(file, isSet=False):
    info = [0, 0, 0, 0, 0, 'RGB', 'A4']
    b = GetFileByte(file)
    kb = int(b / 1024) + (1 if (b % 1024) else 0)
    try:
        image = Image.open(file)
    except Exception as e:
        print(e)
        return (kb, 0, 0, 0, 0, 'RGB', 'A4')
    if 'dpi' in image.info:
        if image.info['dpi'][0] < 300 or image.info['dpi'][1] < 300:
            if isSet:
                image.save(file, dpi=(300, 300))
        return tuple([kb]) + image.size + (image.info['dpi'][0], image.info['dpi'][1]) + \
               tuple([image.mode, getPaper(image.size, (image.info['dpi'][0], image.info['dpi'][1]))])
    else:
        if isSet:
            image.save(file, dpi=(300, 300))
        # image = Image.open(file)
        return tuple([kb]) + image.size + (0, 0) + \
               tuple([image.mode, getPaper(image.size, [0, 0])])


def resizeImage(nameA, nameB):
    img = getImgFile(nameA, False)
    image = Image.open(nameB)
    new_image = image.resize((img[0], img[1]), Image.ANTIALIAS)
    new_image.save(nameB, dpi=(300, 300))


def showLabelImage(area, label, name, self):
    img = QImage()
    img.load(name)
    if self:
        w = area.width()
        h = area.height()
        new_img = img.scaled(w, h, Qt.KeepAspectRatio)
        label.setPixmap(QPixmap.fromImage(new_img))
    else:
        label.setPixmap(QPixmap.fromImage(img))


def writeXlsx(name, data):
    try:
        write = xlsxwriter.Workbook(name)
        sheet = write.add_worksheet()
        for i, cols in enumerate(data):
            for j, d in enumerate(cols):
                if isinstance(d, list):
                    l = len(d)
                    for k, dd in enumerate(d):
                        sheet.write(i, j * l + k, dd)
                else:
                    sheet.write(i, j, d)
        write.close()
    except Exception as err:
        print(err)


def writeCsv(name, data):
    try:
        file_csv = codecs.open(name, 'w+', 'utf-8')
        writer = csv.writer(file_csv, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
        for d in data:
            writer.writerow(d)
    except Exception as err:
        print(err)


def readXlsx(name, sheet=None, title=False):
    data = []
    nt = 0
    if os.path.exists(name):
        read = openpyxl.load_workbook(name)
        sheets = read.get_sheet_names()
        sheetData = read.get_sheet_by_name(sheet if (sheet != None) and (sheet in sheets) else sheets[0])
        rows = sheetData.rows
        n = 0
        for row in rows:
            n += 1
            if n == 1 and title:
                # 标题行
                nt += 1
                continue
            line = [col.value for col in row]
            if line[0] == '日期':
                # 标题行
                nt += 1
                continue
            data.append(line)
    return nt, data


def readXlsxSheet(name):
    sheets = []
    if os.path.exists(name):
        read = openpyxl.load_workbook(name)
        sheets = read.get_sheet_names()
    return sheets


def readCsv(name, type=[]):
    data = []
    if os.path.exists(name):
        if os.path.exists(name):
            with open(name, encoding='utf-8') as csvfile:
                csv_reader = csv.reader(csvfile)
                cmd = 'data.append(row)'
                if len(type) > 0:
                    cmd = 'data.append(['
                    for i, t in enumerate(type):
                        if t == 'int':
                            cmd += 'int(row[%d]),' % (i)
                            continue
                        if t == 'float':
                            cmd += 'float(row[%d]),' % (i)
                            continue
                        cmd += 'row[%d],' % (i)
                    cmd = cmd[:-1] + '])'
                for row in csv_reader:
                    exec(cmd)
                    # data.append(row)
    return data


class MyParser(ConfigParser):
    "Inherit from built-in class: ConfigParser"

    def optionxform(self, optionstr):
        "Rewrite without lower()"
        return optionstr


class bxgConf():
    def __init__(self, file):
        self.initVar()
        self.config = MyParser()
        self.name = file
        self.readConf()

    def initVar(self):
        # 设计配置
        self.varConf = {'Title': '北下关文件',
                        'VoucherPath': '',
                        'AppendixPath': '',
                        'MergePath': '',
                        'OcrPath': '',
                        'ConfFile': '',
                        'ConfSheet': '',
                        'WindowMax': 0}

    def getConfig(self, section, key):
        return self.config.get(section, key)

    def readConf(self):
        self.config.read(self.name, 'utf-8')
        # [CONF]
        for k in self.varConf:
            str = self.getConfig('CONF', k)
            if isinstance(self.varConf[k], int):
                self.varConf[k] = int(str)
            else:
                self.varConf[k] = str

    def writeConf(self, key, value):
        self.config.set('CONF', key, str(value))
        with open(self.name, 'w', encoding='utf-8') as f:
            self.config.write(f)


# 扫描线程
class ScanSrcFileListThread(QThread):
    update_item = pyqtSignal(list)

    def __init__(self, srcList, csvName):
        super().__init__()
        self.srcList = srcList
        self.csvName = csvName

    def run(self):
        if len(self.srcList) == 0:
            self.update_item.emit(['读取配置...'])
            self.srcList = readCsv(self.csvName)
        self.update_item.emit(['配置[%d]' % (len(self.srcList))])
        for i, f in enumerate(self.srcList):
            flist = [i]
            if len(f) == 1:
                flist.extend(['', '', f[0], '0', '0', '0', '0', '0', '[]', '[]'])
            else:
                flist.extend(f)
            self.update_item.emit(flist)
        self.update_item.emit([])


# 处理线程
class ProcFileListThread(QThread):
    update_item = pyqtSignal(list)

    def __init__(self, procDict):
        super().__init__()
        self.dict = procDict

    def run(self):
        for k, v in self.dict.items():
            if v == '0000':
                continue
            time.sleep(0.5)
            self.update_item.emit([k])
        self.update_item.emit([])


def getOcrResultFile(retFile):
    files = []
    datas = []
    with open(retFile, 'r', encoding='utf-8') as f:
        data = f.read()
        dict = ast.literal_eval(data)
        files = list(dict.keys())
        # print(files)
        datas = list(dict.values())
    return getOcrResult(files, datas)


imageColTitle = ['图片名称', '行组数', '外框坐标']
resultLineTitle = ['图片名称', '行内容', '行字数', '行外框坐标', '行类型', '字符内容(字,坐标,类型,分数)']


def getRectFromCoords(coord):
    rect = [coord[0], coord[1], coord[2], coord[7]]
    return rect


# 过滤无效符号【单引号，双引号，正反斜杠】
def filterChar(s):
    filterChar = ('\\', '\'', '"')
    # fileerFantiHz = ('戶', '記', '編')
    # fileerJantiHz = ('户', '记', '编')
    for c in filterChar:
        s = s.replace(c, " ")
    # for i, c in enumerate(fileerFantiHz):
    #     s = s.replace(c, fileerJantiHz[i])
    return s


# 获取识别结果
def getOcrResult(imgfile, data):
    # 行：
    xls_data = [{}, {}]
    file = {}
    ocr_text = ''
    for i in range(len(imageColTitle)):
        file[imageColTitle[i]] = []
    for s, f in enumerate(imgfile):
        file[imageColTitle[0]].append(f)
        file[imageColTitle[1]].append(len(data[s]['lines']))
        file[imageColTitle[2]].append(getRectFromCoords(data[s]['coords']))
    xls_data[0] = file
    lines = {}
    for i in range(len(resultLineTitle)):
        lines[resultLineTitle[i]] = []
    for s, f in enumerate(imgfile):
        chars = data[s]['lines']
        for r, value in enumerate(chars):
            # 串：
            lines[resultLineTitle[0]].append(xls_data[0][imageColTitle[0]][s])
            lines[resultLineTitle[1]].append(filterChar(value['text']))
            lines[resultLineTitle[2]].append(len(value['chars']))
            lines[resultLineTitle[3]].append(getRectFromCoords(value['coords']))
            lines[resultLineTitle[4]].append(value['type'])
            # 字：
            char = []
            for c in value['chars']:
                char.append([filterChar(c['code']), getRectFromCoords(c['coords']), c['type'], c['score']])
            lines[resultLineTitle[5]].append(char)
            # lines[resultLineTitle[5]].append(value['score'])
            ocr_text += lines[resultLineTitle[1]][r]
    xls_data[1] = lines
    return xls_data, ocr_text
